package com.liaoyifan.core.opera;

import jakarta.servlet.Filter;
import java.util.Comparator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletContextInitializerBeans;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;

public class PrintService implements ApplicationRunner {

    private static final Logger log = LogManager.getLogger(PrintService.class.getSimpleName());

    @SuppressWarnings("unused")
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void run(ApplicationArguments args) {
        try {
            this.printConfiguration();
            this.printFilter();
            this.printAspect();
            this.printControllerAdvice();
        } catch (Exception e) {
            log.warn(e.getMessage());
        }
    }

    private void printConfiguration() {
        this.applicationContext.getBeansWithAnnotation(Configuration.class).values().stream()
                .filter(
                        i -> {
                            String packageName = i.getClass().getPackageName();
                            return !(packageName.startsWith("com.liaoyifan.core.aspect")
                                    || packageName.startsWith("com.liaoyifan.core.filter"));
                        })
                .forEach(i -> log.info("Configuration: {}", i.getClass().getName()));
    }

    private void printFilter() {
        new ServletContextInitializerBeans(
                        ((AnnotationConfigServletWebServerApplicationContext)
                                        this.applicationContext)
                                .getBeanFactory())
                .stream()
                        .filter(bean -> bean instanceof FilterRegistrationBean)
                        .map(bean -> (FilterRegistrationBean<?>) bean)
                        .sorted(Comparator.comparingInt(FilterRegistrationBean::getOrder))
                        .forEach(
                                reg -> {
                                    Filter filter = reg.getFilter();
                                    log.info(
                                            "Filter: {}, Order: {}",
                                            filter.getClass().getName(),
                                            reg.getOrder());
                                });
    }

    private void printAspect() {
        this.applicationContext.getBeansWithAnnotation(Aspect.class).values().stream()
                .sorted((a, b) -> Integer.compare(getOrderValue(a), getOrderValue(b)))
                .forEach(
                        i ->
                                log.info(
                                        "Aspect: {}, Order: {}",
                                        i.getClass().getName(),
                                        getOrderValue(i)));
    }

    private void printControllerAdvice() {
        this.applicationContext
                .getBeansWithAnnotation(ControllerAdvice.class)
                .values()
                .forEach(i -> log.info("ControllerAdvice: {}", i.getClass().getName()));
    }

    private int getOrderValue(Object i) {
        if (i instanceof Ordered ordered) {
            return ordered.getOrder();
        }
        if (i.getClass().isAnnotationPresent(Order.class)) {
            return i.getClass().getAnnotation(Order.class).value();
        }
        return Ordered.LOWEST_PRECEDENCE;
    }
}
